package com.li.away3dlite.camera
{
import away3dlite.cameras.Camera3D;

import com.li.away3dlite.events.OldCameraControllerEvent;

import flash.events.EventDispatcher;
import flash.geom.Vector3D;

/**
 * @author Li
 */
public class OldOrbitCameraController extends EventDispatcher
{
    // ------------------------------------------------------------------------------------
    // Private fields.
    // ------------------------------------------------------------------------------------

    private var _camera:Camera3D;
    private var _center:Vector3D;
    private var _targetSphericalCoordinates:Vector3D;
    private var _realSphericalCoordinates:Vector3D;
    private var _evt:OldCameraControllerEvent;
    private var _moving:Boolean;
    private var _dx:Number;
    private var _dy:Number;
    private var _dz:Number;

    private var _minElevation:Number = -0.9*Math.PI;
    private var _maxElevation:Number = -0.1*Math.PI;
    private var _maxRadius:Number = 10000;
    private var _sphericalEasing:Number = 0.25;

    // ------------------------------------------------------------------------------------
    // Public methods.
    // ------------------------------------------------------------------------------------

    public function set maxRadius(value:Number):void
    {
        _maxRadius = value;
    }

    public function OldOrbitCameraController(camera:Camera3D)
    {
        _camera = camera;
        _center = new Vector3D();

        var initialSphericalCoordinates:Vector3D = cartesianToSpherical(_camera.position);
        _targetSphericalCoordinates = initialSphericalCoordinates;
        _realSphericalCoordinates = initialSphericalCoordinates.clone();
    }

    public function get moving():Boolean
    {
        return _moving;
    }

    public function set center(value:Vector3D):void
    {
        _center.x = value.x;
        _center.y = value.y;
        _center.z = value.z;
    }

    public function get center():Vector3D
    {
        return _center.clone();
    }

    public function moveCenterX(value:Number):void
    {
        _center.x += value;
    }

    public function moveCenterY(value:Number):void
    {
        _center.y += value;
    }

    public function moveCenterZ(value:Number):void
    {
        _center.z += value;
    }

    public function moveAzimuth(value:Number):void
    {
        evaluateMotionsStart();
        _targetSphericalCoordinates.x += value;
    }

    public function moveElevation(value:Number):void
    {
        evaluateMotionsStart();
        var targetElevation:Number = _targetSphericalCoordinates.y + value;
        targetElevation = targetElevation < _minElevation ? _minElevation : targetElevation;
        targetElevation = targetElevation > _maxElevation ? _maxElevation : targetElevation;
        _targetSphericalCoordinates.y = targetElevation;
    }

    public function moveRadius(value:Number):void
    {
        evaluateMotionsStart();

        _targetSphericalCoordinates.z += value
        _targetSphericalCoordinates.z = _targetSphericalCoordinates.z < _maxRadius ? _targetSphericalCoordinates.z : _maxRadius;
    }

    public function update():void
    {
        _dx = _targetSphericalCoordinates.x - _realSphericalCoordinates.x;
        _dy = _targetSphericalCoordinates.y - _realSphericalCoordinates.y;
        _dz = _targetSphericalCoordinates.z - _realSphericalCoordinates.z;

        if(Math.abs(_dx) < 0.01 && Math.abs(_dy) < 0.01 && Math.abs(_dz) < 0.01)
        {
            evaluateMotionFinish();
        }

        _realSphericalCoordinates.x += _dx*_sphericalEasing;
        _realSphericalCoordinates.y += _dy*_sphericalEasing;
        _realSphericalCoordinates.z += _dz*_sphericalEasing;

        var cartesianCoordinates:Vector3D = sphericalToCartesian(_realSphericalCoordinates);
        _camera.x = cartesianCoordinates.x;
        _camera.y = cartesianCoordinates.y;
        _camera.z = cartesianCoordinates.z;

        _camera.lookAt(_center);
    }

    // ------------------------------------------------------------------------------------
    // Private methods.
    // ------------------------------------------------------------------------------------

    private function evaluateMotionsStart():void
    {
        if(_moving)
            return;

        _moving = true;
        notifyMotionStarted();
    }

    private function evaluateMotionFinish():void
    {
        if(!_moving)
            return;

        _moving = false;
        notifyMotionFinished();
    }

    public function set maxElevationFromPosition(value:Vector3D):void
    {
        var sphericalContained:Vector3D = cartesianToSpherical(value);
    }

    private function sphericalToCartesian(sphericalCoords:Vector3D):Vector3D
    {
        var cartesianCoords:Vector3D = new Vector3D();
        cartesianCoords.x = _center.x + sphericalCoords.z*Math.sin(sphericalCoords.x)*Math.sin(sphericalCoords.y);
        cartesianCoords.y = _center.y + sphericalCoords.z*Math.cos(sphericalCoords.y);
        cartesianCoords.z = _center.z + sphericalCoords.z*Math.cos(sphericalCoords.x)*Math.sin(sphericalCoords.y);
        return cartesianCoords;
    }

    private function cartesianToSpherical(cartesianCoords:Vector3D):Vector3D
    {
        var cartesianFromCenter:Vector3D = new Vector3D();
        cartesianFromCenter.x = cartesianCoords.x - _center.x;
        cartesianFromCenter.y = cartesianCoords.y - _center.y;
        cartesianFromCenter.z = cartesianCoords.z - _center.z;
        var sphericalCoords:Vector3D = new Vector3D();
        sphericalCoords.z = Math.sqrt(cartesianFromCenter.x*cartesianFromCenter.x + cartesianFromCenter.y*cartesianFromCenter.y + cartesianFromCenter.z*cartesianFromCenter.z);
        sphericalCoords.x = Math.atan2(cartesianFromCenter.x, cartesianFromCenter.z) - Math.PI;
        sphericalCoords.y = -Math.acos((cartesianFromCenter.y)/sphericalCoords.z);
        return sphericalCoords;
    }

    private function notifyMotionStarted():void
    {
        _evt = new OldCameraControllerEvent(OldCameraControllerEvent.MOTION_STARTED);
        dispatchEvent(_evt);
    }

    private function notifyMotionFinished():void
    {
        _evt = new OldCameraControllerEvent(OldCameraControllerEvent.MOTION_FINISHED);
        dispatchEvent(_evt);
    }
}
}
